/***************************************************************************
** @file:		rs485.c
** @brief:  	
** @author:
** @date:		2016-11-29
** @version: 	
***************************************************************************/
#include "rs485.h"
#include "intrins.h"
#include "crc16.h"

static bit flagOnceTxd = 0;  // 单次发送完毕
static bit cmdArrived = 0; 	// 接收到数据

static unsigned char cntRxd = 0;
static unsigned char idata bufRxd[40]; // 串口接收缓冲区


unsigned char regGroup[5];  //Modbus 寄存器组 地址0x0000 ~ 0x0004


static void DelayX10us(uint8_t t)  //软件延迟(t*10)us
{
    do {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    } while (--t);
}



void rs485_init(void) /*串口初始化，波特率设置为9600bit/s*/
{
	RS485_DIR = 0; //RS485方向设置为接收
	
	/*设置波特率*/
	TMOD=0x20|TMOD;//定时器1方式2
	TH1=0xfd; //11.0592M晶振
	TL1=0xfd;

	TR1=1;//启动定时器
	ET1=0; //定时器1中断禁止

	/*设置串口工作方式*/
	SCON=0x50; //串口方式1 允许接收
	PCON=0x00; //波特率不加倍
	IP |= 0x10; //串口中断优先级最高
 
	ES=1;  		//开串口中断 发送采用查询方式 接收采用中断方式	
	EA=1;  		//开总中断

}

void rs485_send_byte(uint8_t ddata) /*向串口发送一个字节数据*/
{
	RS485_DIR = 1; 	// 设置为发送
	SBUF=ddata;
	while(!flagOnceTxd); //等待发送完毕
	DelayX10us(5); 
	RS485_DIR = 0;	// 设置为接收
	
}


void rs485_send_str(uint8_t* buf,int8_t len)
{

  RS485_DIR = 1; 	// 设置为发送
	// 发送数据
	while(len--)
	{
		flagOnceTxd = 0;
		SBUF=*buf;
		while(!flagOnceTxd); //等待发送完毕
		buf++;
		
	}
	
	DelayX10us(5); 
	RS485_DIR = 0;	// 设置为接收
}


void uart_RxMonitor(uint8_t ms)  // 串口接收监控函数
{
    static uint8_t last_cntRxd = 0;
    static uint8_t idletmr = 0;

    if (cntRxd > 0)  // 有接收到数据
    {
        if (last_cntRxd != cntRxd)  // 上一次的接收计数器的值和当前的值不一样，即正在接收数据
        {
            last_cntRxd = cntRxd;		// 保存当前的计数器值
            idletmr = 0;						// 空闲时间重新计算
        }
        else	// 计数器的值未改变，即总线空闲，开始计时空闲时间
        {
            if (idletmr < 5)  //
            {
                idletmr += ms;
                if (idletmr >= 5)  // 空闲时间大于或等于5ms则认为一帧结束
                {
                    cmdArrived = 1; // 接收到一帧数据了
                }
            }
        }
    }
    else
    {
        last_cntRxd = 0;
    }
}


uint8_t rs485_read(uint8_t* buf,uint8_t len) // 串口读取接收缓冲区
{
    unsigned char i;
    
    if (len > cntRxd) // 读取的长度大于接收缓冲区，则截断
    {
        len = cntRxd; 
    }
    for (i=0; i<len; i++) // 拷贝接收到的数据到buf缓冲区
    {
        *buf = bufRxd[i];
        buf++;
    }
    cntRxd = 0;  // 清空计数器
    
    return len;  // 返回长度
}

// 使用modbus协议
void frame_disp(void) //  帧处理
{
	uint8_t i;
	uint8_t cnt;
	uint8_t len;
	uint8_t buf[30];
	uint16_t crc;
	
	if(cmdArrived==1) // 若有命令到达
	{
		
		cmdArrived = 0;
		len = rs485_read(buf, sizeof(buf));		// 将接收到命令读取到接收缓冲区
		// 进入帧处理
		if(GetCRC16(buf,len)!=0)
		{
			// crc 校验错误
			return ; //退出
		}
		
		// 判断是否发送给本机的
		if (buf[0] != 0x01)  // 判断地址是否是本地地址
		{
			return ;
		}
		
		// 按功能码执行操作
		switch (buf[1])
		{
			case 0x03:
			if ((buf[2] == 0x00) && (buf[3] <= 0x05)) // 寄存器地址范围0x0000~0x0005
			{
				if (buf[3] <= 0x04)
				{
						i = buf[3];      // 提取寄存器的地址
						cnt = buf[5];    // 提取待读取的寄存器的个数
						buf[2] = cnt*2;  // 读取数据的寄存器字节数 = 寄存器数*2, 因为modbus 定义的寄存器为16位
						len = 3;
						while (cnt--)
						{
							buf[len++] = 0x00;          // 寄存器的高字节补0
							buf[len++] = regGroup[i++]; // 寄存器的低字节补0
						}
					}
				else  // 地址为5为蜂鸣器的状态
				{
					buf[2] = 2;  // 读取的字节数
					buf[3] = 0x00;
					buf[4] = 1; // 假设蜂鸣器的状态为1
					len = 5;
				}
				break;
			}
			else  // 寄存器不被支持 返回错误码
			{
				buf[1] = 0x83;  // 功能码最高位置1
				buf[2] = 0x02;  // 设置异常码为2
				len = 3;
				break;
			}

			default:
				break;

		}
		
		// 回复应答
		crc = GetCRC16(buf, len); //??CRC???
		buf[len++] = crc >> 8;    //CRC 高字节
		buf[len++] = crc & 0xFF;  //CRC 低字节
		rs485_send_str(buf, len);      //?????
		
		// 

	}
}



//串口中断程序
void serial_inter() interrupt 4
{
	uint8_t tmp = 0;

	//串口接收中断
	if(RI==1)
	{
		RI = 0;
		tmp = SBUF;
			
		if (cntRxd < sizeof(bufRxd)) // 接收缓冲区尚未用完
		{
			bufRxd[cntRxd++] = SBUF; //接收数据存放到接收缓冲区
		}
	}
	
	if(TI==1)	 
	{
		TI = 0;
		flagOnceTxd = 1;
	}				   
}

/*****************end of file  rs485.c***********/
